import Foundation

/**
 * 闭包
 * 1. 自包含的函数代码块, 可以在代码中被传递和使用.
 * 2. 可以捕获和存储其所在上下文中任意常量和变量的引用, 被称为`包裹常量`和`包裹变量`
 * 3. Swift会自动管理捕获过程中涉及到的所有内存操作
 *
 * 全局和嵌套函数, 也是一种特殊的闭包
 */

/**
 * `全局函数`: 是一个有名字,但不会捕获任何值的闭包
 * `嵌套函数`: 是一个有名字并且可以`捕获其封闭函数域内值`的闭包
 * `闭包表达式`: 是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
 */

/** 闭包的语法优化
 * 1. 利用上下文`推断参数和返回值类型`
 * 2. `隐式返回`单表达式闭包, 即但表达式闭包可以省略return关键字
 * 3. 参数`名称缩写`
 * 4. `尾随闭包`语法
 */


// MARK: - 闭包表达式
public func closureDemo_sort() {
    // 1. 原始数组
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    
    // 2. 定义函数及其实现
    func backward(s1: String, s2: String) -> Bool { s1 > s2 }
    
    // 3. 为sorted函数传入一个函数(sorted方法接受一个闭包)
    let reversedNames = names.sorted(by: backward)
    
    print(reversedNames)
}

// MARK: - 闭包表达式语法
/*
 {  (paramters) -> return type in
        statement
 }
*/

/**
 in: 表示闭包的参数和返回值类型`已定义完成`
 */

func closureExample_1() {
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    // 基础版本
    var reversedNames = names.sorted { s1, s2 in
        return s1 > s2
    }
    // 完整语法版本
    reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
        s1 > s2
    })
    print("example_1: \(reversedNames)")
}

// MRAK: - 根据上下文推断类型
public func closureExample_2() {
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    // 因为排序闭包函数是所谓sorted(by:)方法的参数传入的
    // Swift可以推断其参数和返回值的类型.
    // 所以, 通过内联闭包表达式构造的闭包作为参数传递给函数或方法时, 总能够推断出闭包的参数和返回值类型
    // 写代码时, 几乎不需要利用完整格式构造内联闭包
    let reversedNames = names.sorted { s1, s2 in
        // 单行表达式时, 可以省略return来隐式的返回
        s1 > s2
    }
    print("example_2: \(reversedNames)")
}

// MARK: - 参数名称缩写
func closureExample_3() {
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    // Swift自动为内联闭包提供了参数名称缩写, 可以直接通过`$0`, `$1`, `$2` 来顺序调用闭包的参数, 以此类推
    let reversedNames = names.sorted(by: { $0 > $1 })
    print("example_3: \(reversedNames)")
}

// MARK: - 运算符方法
func closureExample_4() {
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    // Swift的String类型定义了关于大于号(>)的字符串实现
    // 所以可以直接传一个 >
    let reversedNames = names.sorted(by: >)
    print("example_4: \(reversedNames)")
}


// MARK: - 尾随闭包
/// 如果需要将一个很长的闭包表达式作为最后一个参数传递给函数, 将这个闭包替换为尾随闭包的形式很有用
public func closureExample_5() {
    func someFunctionThatTakesAClosure(closure: () -> Void) {
        closure()
    }
    
    func takeClosure(closure: (Int, Int) -> Void) {
        closure(3, 4)
    }
    
    // 1. 普通调用
    someFunctionThatTakesAClosure(closure: {
        print("普通写法")
    })
    
    // 2. 尾随闭包式调用
    someFunctionThatTakesAClosure() {
        print("尾随闭包写法")
    }
    takeClosure() { a, b in
        
    }
    
    
    // 3. 如果闭包式函数唯一的参数, 可以吧()也省略掉
    someFunctionThatTakesAClosure {
        print("尾随闭包写法_省略()")
    }
    

    let digitNames = [
        0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
        5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
    ]
    let numbers = [16, 58, 510]
    let strings = numbers.map { number -> String in
        var number = number
        var output = ""
        repeat {
            output = digitNames[number % 10]! + output
            number /= 10
        } while number > 0
        return output
    }
    print("strings: \(strings)")
}

// MARK: - 值捕获
/// 1. 闭包可以在其被定义的上下文中捕获`常量`和`变量`
/// 2. 即使定义这些常量和变量的原作用域已不存在, 闭包仍然可以在闭包函数体内引用和修改这些值

/// Swift中最简单的捕获值的形式是嵌套函数
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        // 捕获了runningTotal和amount
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

public func closureExample_6() {
    // 设置为每步增加 10
    let incrementByTen = makeIncrementer(forIncrement: 10)
    // +10
    var result = incrementByTen()
    print("example_6 \(result)")
    
    // +10
    result = incrementByTen()
    print("example_6 \(result)")
    
    // +10
    result = incrementByTen()
    print("example_6 \(result)")
    
    let incrementBySeven = makeIncrementer(forIncrement: 7)
    let result2 = incrementBySeven()
    print("example_6 \(result2)")
    
    let alsoIncrementByTen = incrementByTen
    let resule3 = alsoIncrementByTen()
    print("example_6 \(resule3)")
}


// MARK: - 闭包是引用类型(函数也是)


// MARK: - 逃逸闭包
/// 1. 当一个`闭包作为参数`传到一个函数中
/// 2. 这个闭包在`函数返回之后才执行`
/// 3. 此闭包就被称为`逃逸闭包`
/// 4. 在参数名之前标注`@escaping`, 指明这个闭包允许逃逸出这个函数

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
        // 仅添加一个闭包,并未执行
        someFunctionWithEscapingClosure {
            self.x = 100
        }

        // 添加并执行这个闭包
        someFunctionWithNonescapingClosure {
            /// 由于使用的是非逃逸闭包
            /// 就可以隐式的应用`self`
            x = 200
        }
    }
}

public func closureExample_7() {
    let instance = SomeClass()
    instance.doSomething()
    print("example_7: \(instance.x)")
    
    completionHandlers.first?()
    print("example_7: \(instance.x)")
}


// MARK: - 自动闭包
/// 1. 一种自动创建的闭包
/// 2. 用于包装传递给函数作为参数的表达式, 这种闭包`不接受任何参数`
/// 3. 被调用时, 会返回被包装在其中的表达式的值.
/// 4. 这种语法能够省略闭包的花括号, 用一个普通的表达式来替代显式的闭包.
/// 5. 自动闭包能够延迟求值
public func closureExample_8() {
    var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    print(customersInLine.count)
    
    let customerProvider = { customersInLine.remove(at: 0) }
    print(customersInLine.count)
    
    // customerProvider不调用时, 闭包表达式永远不会执行
    print("Now serving \(customerProvider())")
    print(customersInLine.count)
}

public func closureExample_9_autoclosure() {
    var customersInLine = ["Alex", "Ewa", "Barry", "Daniella"]

    // 将闭包作为参数传递给函数, 同样可以获得延迟求值行为
    func serve(customer customerProvider: () -> String) {
        print("Now serving \(customerProvider())")
    }
    // 调用
    serve {
        customersInLine.remove(at: 0)
    }
    
    // 使用自动闭包
    func serveByAutoclosure(customer customerProvider: @autoclosure () -> String) {
        print("Now serving \(customerProvider())")
    }
    // 调用包含自动闭包的函数(省略了花括号)
    serveByAutoclosure(customer: customersInLine.remove(at: 0))
}




func methodCallback(closure: @escaping () -> Void) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
        closure()
    })
}

public func demo_closure() {
    print("start at \(Date())")
    methodCallback {
        print("call back at \(Date())")
    }
}

